package character;

import action.Action;
import action.ActionMove;
import board.BoardUtil;
import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.animation.LoopMode;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
import game.DefaultValues;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import misc.Vector3i;


public abstract class MyCharacterControl extends CharacterControl implements AnimEventListener {
    public static final int ANIM_CHANNEL_WALK = 0;
    public static final String ANIM_NAME_STAND = "stand";
    public static final String ANIM_NAME_WALK = "Walk";
    public static final float ANIM_BLEND_TIME = 0.3f;
    protected MyCharacter myCharacter;
    protected Deque<Action> queuedActions = new ArrayDeque<Action>();
    
    @Deprecated
    protected MyCharacterControl() {}
    
    public MyCharacterControl(CollisionShape collisionShape) {
        super(collisionShape, DefaultValues.CHARACTER_SETTING_STEP_HEIGHT);
        init();
    }
    
    private void init() {
	setGravity(DefaultValues.CHARACTER_GRAVITY);
	setJumpSpeed(DefaultValues.CHARACTER_JUMP_SPEED);
	setFallSpeed(DefaultValues.CHARACTER_FALL_SPEED);
        setMaxSlope(DefaultValues.CHARACTER_SETTING_MAX_SLOPE);
    }
    
    public boolean tick() {
	if(hasQueuedAction()) {
            return queuedActions.peekFirst().tickAction();
	}
        return true;
    }
    
    public boolean hasQueuedAction() {
	return !queuedActions.isEmpty();
    }
    public Action peekQueuedAction() {
	return queuedActions.peekFirst();
    }
    public void queueActionFirst(Action action) {
	queuedActions.addFirst(action);
    }
    public void queueActionLast(Action action) {
        queuedActions.addLast(action);
    }
    public void abortQueuedActions() {
        Iterator<Action> iter = queuedActions.iterator();
        while(iter.hasNext()) {
            Action action = iter.next();
            if(action.isStarted() && !action.isCompleted())
                action.abortAction();
            iter.remove();
        }
    }
    
    public void setWalk(Vector3f destination, float speed) {
        Vector3f moveDir = destination.subtract(getPhysicsLocation());
        setWalkDirection(moveDir.normalizeLocal().multLocal(speed));
        updateWalkAnimation();
    }
    public void stopWalking() {
        setWalkDirection(Vector3f.ZERO);
        updateWalkAnimation();
    }
    
    public void doRandomMove() {
        List<Vector3i> validMoveLocations = new ArrayList<Vector3i>(getValidMoveLocations());
        if(!validMoveLocations.isEmpty()) {
            Vector3i destination = validMoveLocations.get(FastMath.rand.nextInt(validMoveLocations.size()));
            
            Collection<Vector3i> travelPath = BoardUtil.findPath(myCharacter.getBoardLocation(), destination, myCharacter.getCharacterStats().getMoveSpeedWithModifier(), myCharacter.getCharacterStats().getDiagonalMoveSpeedWithModifier());
            for(Vector3i waypoint : travelPath) {
                queueActionLast(new ActionMove(myCharacter, waypoint));
                myCharacter.setQueuedLocation(waypoint);
            }
            System.out.println("AI choosing destination "+destination);
        }
    }
    
    public void updateWalkAnimation() {
	AnimControl animControl = myCharacter.getModel().getControl(AnimControl.class);
	if(animControl != null) {
	    AnimChannel walkChannel = animControl.getChannel(ANIM_CHANNEL_WALK);
	    if(!walkDirection.equals(Vector3f.ZERO)) {
		if(!walkChannel.getAnimationName().equals(ANIM_NAME_WALK)) {
		    walkChannel.setAnim(ANIM_NAME_WALK, ANIM_BLEND_TIME);
		}
	    } else {
		walkChannel.setAnim(ANIM_NAME_STAND, ANIM_BLEND_TIME);
	    }
	}
    }
    
    public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
	if(channel.getLoopMode() == LoopMode.DontLoop) {
	    channel.setAnim(ANIM_NAME_STAND, ANIM_BLEND_TIME);
	}
    }

    public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {}
    
    public abstract Collection<Vector3i> getValidMoveLocations();
    public abstract void repopulateValidMoveLocations();
    
    @Override
    public void setSpatial(Spatial spatial) {
        super.setSpatial(spatial);
        if(spatial != null) {
            myCharacter = (MyCharacter)spatial;
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final MyCharacterControl other = (MyCharacterControl) obj;
        if (this.myCharacter != other.myCharacter && (this.myCharacter == null || !this.myCharacter.equals(other.myCharacter))) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 79 * hash + (this.myCharacter != null ? this.myCharacter.hashCode() : 0);
        return hash;
    }
}
